package com.baoyouqun.API.task;

import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baoyouqun.API.friend.FriendRelationService;
import com.baoyouqun.API.friend.domain.FriendRelation;
import com.baoyouqun.domain.Enum.ResourceTypeEnum;
import com.baoyouqun.domain.VO.PostTypeDTO;
import com.baoyouqun.domain.VO.UserResourceVO;
import com.baoyouqun.domain.VO.UserVO;
import com.baoyouqun.entity.Demand;
import com.baoyouqun.entity.User;
import com.baoyouqun.service.*;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

//@Service
@Slf4j
@SaIgnore
//@RequiredArgsConstructor
//@RestController("task")
public class UserScoreScheduledTask {

    // 线程池：并行计算100条用户分数（核心线程数=10，避免资源耗尽）
    private final ExecutorService executor = Executors.newFixedThreadPool(10);

    @Resource
    private UserService userService;
    @Resource
    private UserVipTimeService userVipTimeService;
    @Resource
    private UserResourceService userResourceService;
    @Resource
    private DemandInteractService demandInteractService;
    @Resource
    private ReportRecordService reportRecordService;

    @Resource
    private UserRecommendService userRecommendService;
    @Resource
    private FriendRelationService friendRelationService;
    @Resource
    private UserSearchRecordService userSearchRecordService;
    @Resource
    private SystemConfigService systemConfigService;
    @Resource
    private DemandService demandService; // 假设存在该服务用于查询帖子

    /**
     * 定时任务：每5分钟抓取20条用户计算推荐好友1000个
     * 修正cron表达式为每5分钟执行一次
     */
//    @RequestMapping("countUserReferralsNew")
    @Scheduled(cron = "0 */5 * * * ?")
    @Transactional
    public void countUserReferralsNew() {
        log.info("开始执行用户推荐好友定时任务...");
        try {
            // 步骤1&2：获取20个待计算用户（优先未计算的，再按登录时间混合选取）
            List<String> targetUserIds = getUserIdsForCalculation(20);
            if (targetUserIds.isEmpty()) {
                log.info("无符合条件的待计算用户，任务结束");
                return;
            }
            log.info("本次待计算用户数量：{}", targetUserIds.size());

            // 为每个目标用户计算推荐好友
            for (String targetUserId : targetUserIds) {
                calculateAndSaveRecommendations(targetUserId);
            }

        } catch (Exception e) {
            log.error("用户推荐好友定时任务执行失败", e);
        }
    }

    /**
     * 获取待计算的用户ID列表
     */
    private List<String> getUserIdsForCalculation(int limit) {
        // 1. 优先获取未计算过推荐的用户（假设User表有lastRecommendTime字段）
        List<String> uncalculatedUsers = userService.selectUncalculatedUserIds(limit);
        if (uncalculatedUsers.size() >= limit) {
            return uncalculatedUsers.subList(0, limit);
        }

        // 2. 不足时，从有登录记录的用户中补充（最近登录的取一半，其他取一半）
        int remaining = limit - uncalculatedUsers.size();
        int recentHalf = (remaining + 1) / 2; // 向上取整
        int otherHalf = remaining / 2;

        List<String> recentLoginUsers = userService.selectRecentLoginUserIds(recentHalf);
        List<String> otherActiveUsers = userService.selectOtherActiveUserIds(otherHalf, recentLoginUsers);

        // 合并结果并去重
        Set<String> resultSet = new LinkedHashSet<>(uncalculatedUsers);
        resultSet.addAll(recentLoginUsers);
        resultSet.addAll(otherActiveUsers);

        return new ArrayList<>(resultSet).subList(0, Math.min(resultSet.size(), limit));
    }

    /**
     * 为单个目标用户计算并保存推荐结果
     */
    private void calculateAndSaveRecommendations(String targetUserId) {
        // 步骤3：获取目标用户的资源供需信息
        List<UserResourceVO> targetResources = userResourceService.selectByUserId(targetUserId);
        Set<String> needResourceName = new HashSet<>();
        Set<String> ownResourceName = new HashSet<>();

        for (UserResourceVO resource : targetResources) {
            if (ResourceTypeEnum.NEED.equals(resource.getResourceTypeEnum())) {
                needResourceName.add(resource.getName());
            } else if (ResourceTypeEnum.OWN.equals(resource.getResourceTypeEnum())) {
                ownResourceName.add(resource.getName());
            }
        }

        // 获取供需互补的候选用户
        Set<String> candidateIds = new HashSet<>();
        if (!needResourceName.isEmpty()) {
            candidateIds.addAll(userResourceService.selectUserIdsByTypeAndResourceIds(
                    ResourceTypeEnum.OWN, needResourceName));
        }
        if (!ownResourceName.isEmpty()) {
            candidateIds.addAll(userResourceService.selectUserIdsByTypeAndResourceIds(
                    ResourceTypeEnum.NEED, ownResourceName));
        }

        // 排除自己
        candidateIds.remove(targetUserId);
        if (candidateIds.isEmpty()) {
            log.info("用户[{}]无符合条件的候选推荐好友", targetUserId);
            return;
        }

        // 步骤8：排除已添加的好友
        List<FriendRelation> friendRelations = friendRelationService.getFriendRelationsByUserId(targetUserId);
        Set<String> friendIds = friendRelations.stream().map(FriendRelation::getFriendId).collect(Collectors.toSet());
        candidateIds.removeAll(friendIds);
        if (candidateIds.isEmpty()) {
            log.info("用户[{}]的候选推荐好友均为已添加好友", targetUserId);
            return;
        }

        // 获取目标用户基础信息
        UserVO targetUser = userService.selectById(targetUserId);
        if (targetUser == null) {
            log.warn("用户[{}]不存在，跳过推荐计算", targetUserId);
            return;
        }

        // 步骤4-7、9：计算候选用户评分
        Map<String, Integer> scoreMap = calculateCandidateScores(
                new ArrayList<>(candidateIds), targetUser);

        // 排序并取前1000个
        List<Map.Entry<String, Integer>> sortedCandidates = scoreMap.entrySet().stream()
                .sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
                .limit(1000)
                .collect(Collectors.toList());

        // 步骤10：保存推荐结果
        saveRecommendations(targetUserId, sortedCandidates);

        // 更新用户最后计算时间
        userService.updateLastRecommendTime(targetUserId, LocalDateTime.now());
    }

    /**
     * 计算候选用户的推荐评分
     */
    private Map<String, Integer> calculateCandidateScores(List<String> candidateIds, UserVO targetUser) {
        Map<String, Integer> scoreMap = new HashMap<>(candidateIds.size());
        if (candidateIds.isEmpty()) return scoreMap;

        // 批量获取候选用户信息
        List<User> candidates = userService.listByIds(candidateIds);
        Map<String, User> candidateMap = candidates.stream()
                .collect(Collectors.toMap(User::getId, u -> u));

        // 获取VIP状态（缓存有效VIP用户ID）
        Set<String> vipUserIds = userVipTimeService.selectValidVipUserIds(candidateIds);

        // 获取目标用户的搜索记录关键词
        List<String> searchKeywords = userSearchRecordService.selectLatestKeywordsByUserId(
                targetUser.getId(), 10); // 取最近10条搜索词

        // 目标用户的地区和行业信息
        String targetProvince = targetUser.getProvince();
        String targetCity = targetUser.getCity();
        String targetDistrict = targetUser.getDistrict();
        String targetIndustry = targetUser.getIndustry();

        // 计算每个候选用户的得分
        for (String candidateId : candidateIds) {
            User candidate = candidateMap.get(candidateId);
            if (candidate == null) continue;

            int score = 0;

            // 步骤4：VIP用户加分
            if (vipUserIds.contains(candidateId)) {
                score += 20;
            }

            // 步骤5：活跃度加分（按比例转换）
            score += Optional.ofNullable(candidate.getActiveScore()).orElse(0) / 5;

            // 步骤6：地区相似度加分（区县>市>省）
            score += calculateAreaScore(candidate, targetProvince, targetCity, targetDistrict);

            // 步骤7：行业相似度加分
            score += calculateIndustryScore(candidate.getIndustry(), targetIndustry);

            // 步骤9：搜索记录匹配加分
            score += calculateSearchMatchScore(candidate, searchKeywords);

            scoreMap.put(candidateId, score);
        }

        return scoreMap;
    }

    /**
     * 计算地区匹配得分
     */
    private int calculateAreaScore(User candidate, String targetProvince,
                                   String targetCity, String targetDistrict) {
        if (Objects.equals(candidate.getDistrict(), targetDistrict) &&
                Objects.equals(candidate.getCity(), targetCity) &&
                Objects.equals(candidate.getProvince(), targetProvince)) {
            return 15; // 区县相同
        } else if (Objects.equals(candidate.getCity(), targetCity) &&
                Objects.equals(candidate.getProvince(), targetProvince)) {
            return 10; // 市相同
        } else if (Objects.equals(candidate.getProvince(), targetProvince)) {
            return 5; // 省相同
        }
        return 0;
    }

    /**
     * 保存推荐结果到数据库
     */

    /**
     * 计算行业匹配得分
     */
    private int calculateIndustryScore(String candidateIndustry, String targetIndustry) {
        if (Objects.isNull(candidateIndustry) || Objects.isNull(targetIndustry)) {
            return 0;
        }
        return candidateIndustry.equals(targetIndustry) ? 12 :
                (candidateIndustry.contains(targetIndustry) || targetIndustry.contains(candidateIndustry)) ? 8 : 0;
    }

    /**
     * 计算搜索记录匹配得分
     */
    private int calculateSearchMatchScore(User candidate, List<String> searchKeywords) {
        if (searchKeywords.isEmpty()) return 0;

        // 收集候选用户的关键信息（用于匹配搜索词）
        Set<String> candidateInfo = new HashSet<>();
        Optional.ofNullable(candidate.getCompany()).ifPresent(candidateInfo::add);
        Optional.ofNullable(candidate.getIndustry()).ifPresent(candidateInfo::add);
        Optional.ofNullable(candidate.getName()).ifPresent(candidateInfo::add);

        // 匹配资源名称
        List<UserResourceVO> candidateResources = userResourceService.selectByUserId(candidate.getId());
        candidateResources.forEach(res -> Optional.ofNullable(res.getName()).ifPresent(candidateInfo::add));

        // 计算匹配得分
        long matchCount = searchKeywords.stream()
                .filter(keyword -> candidateInfo.stream().anyMatch(info -> info.contains(keyword)))
                .count();

        return (int) (matchCount * 10); // 每个匹配的关键词加10分
    }

    /**
     * 保存推荐结果到数据库（适配新的UserRecommend结构）
     */
    private void saveRecommendations(String targetUserId, List<Map.Entry<String, Integer>> sortedCandidates) {
        if (sortedCandidates.isEmpty()) {
            log.info("用户[{}]无推荐好友，无需保存", targetUserId);
            return;
        }

        // 步骤1：提取排序后的推荐用户ID列表（仅保留ID，按评分降序）
        List<String> recommendUserIds = sortedCandidates.stream()
                .map(Map.Entry::getKey) // 取推荐用户ID
                .collect(Collectors.toList());

        // 步骤2：构建UserRecommend对象（一条记录对应一个用户的所有推荐）
        UserRecommend userRecommend = new UserRecommend();
        userRecommend.setUserId(targetUserId); // 目标用户ID
        userRecommend.setRecommendUserIds(recommendUserIds); // 设置推荐ID列表（自动转换为逗号分隔字符串）
        userRecommend.setUpdateTime(LocalDateTime.now()); // 更新时间

        // 步骤3：调用服务层保存（插入或更新，无需先删除旧记录，新结构自动覆盖）
        boolean saveSuccess = userRecommendService.saveOrUpdate(userRecommend);

        if (saveSuccess) {
            log.info("用户[{}]推荐好友保存成功，共{}条推荐", targetUserId, recommendUserIds.size());
        } else {
            log.error("用户[{}]推荐好友保存失败", targetUserId);
        }
    }

    /**
     * 定时任务：每5分钟抓取100条用户并计算活跃度分数
     */
    @Scheduled(cron = "0 */5 * * * ?") // 修正cron表达式为每5分钟执行
    @Transactional
    @RequestMapping("fetchAndCalculateUserScores")
    public void fetchAndCalculateUserScores() {
        log.info("开始执行用户分数计算定时任务...");
        try {
            LocalDateTime now = LocalDateTime.now();
            // 步骤1&2：抓取100条待计算用户（优先未计算的，不足则补充活跃用户）
            List<User> users = userService.getUsersForScoreCalculation(100);
            if (CollUtil.isEmpty(users)) {
                log.info("无符合条件的用户需要计算分数，任务结束");
                return;
            }
            List<String> userIds = users.stream().map(User::getId).collect(Collectors.toList());

            // 预加载所有关联数据（减少N+1查询）
            // 步骤3：VIP状态映射（userId -> 是否有效VIP）
            Map<String, Boolean> vipMap = userVipTimeService.getValidVipMap(userIds, now);
            // 步骤4：登录时间已提前包含在User对象中
            // 步骤5：资源标签数量映射（userId -> 总数量）
            Map<String, Integer> resourceCountMap = userResourceService.countByUserIds(userIds);
            // 步骤6：有效互动次数映射（userId -> 次数）
            Map<String, Integer> interactCountMap = demandInteractService.countValidByUserIds(userIds);
            // 步骤7：被有效举报次数映射（userId -> 次数）
            Map<String, Integer> reportCountMap = reportRecordService.countValidReportsByUserIds(userIds);

            // 步骤9：遍历计算分数
            for (User user : users) {
                String userId = user.getId();
                int score = 0;

                // VIP加分（20分）
                if (Boolean.TRUE.equals(vipMap.getOrDefault(userId, false))) {
                    score += ActivityScoreWeightEnum.VIP_WEIGHT.getWeight();
                }

                // 登录时间加分（按档位计算）
                score += calculateLoginScore(user.getLastLoginTime(), now);

                // 资源标签完善度加分（每项5分，最高20分）
                int resourceCount = resourceCountMap.getOrDefault(userId, 0);
                int resourceScore = resourceCount * ActivityScoreWeightEnum.RESOURCE_PERFECT.getWeight();
                score += Math.min(resourceScore, 20);

                // 互动频率加分（每10次3分，最高15分）
                int interactCount = interactCountMap.getOrDefault(userId, 0);
                int interactScore = (interactCount / 10) * ActivityScoreWeightEnum.INTERACT_FREQUENCY.getWeight();
                score += Math.min(interactScore, 15);

                // 信息完整度加分（每项2分，最高20分）
                int infoCompleteCount = calculateInfoCompleteCount(user);
                int infoScore = infoCompleteCount * ActivityScoreWeightEnum.INFO_COMPLETENESS.getWeight();
                score += Math.min(infoScore, 20);

                // 被举报减分（每次10分）
                int reportCount = reportCountMap.getOrDefault(userId, 0);
                score -= reportCount * ActivityScoreWeightEnum.REPORT_PENALTY.getWeight();

                // 确保分数不为负
                score = Math.max(score, 0);

                // 步骤10：更新分数
                user.setActiveScore(score);
                user.setScoreUpdateTime(now);
            }

            // 批量更新用户分数
            userService.updateBatchById(users);
            log.info("用户分数计算完成，共处理{}名用户", users.size());

        } catch (Exception e) {
            log.error("用户分数计算定时任务执行失败", e);
            // 事务回滚（由@Transactional保证）
            throw new RuntimeException("分数计算任务执行失败", e);
        }
    }

    /**
     * 计算登录时间对应的分数
     */
    private int calculateLoginScore(LocalDateTime lastLoginTime, LocalDateTime now) {
        if (lastLoginTime == null) {
            return ActivityScoreWeightEnum.LOGIN_OVER_30_DAYS.getWeight();
        }
        long days = ChronoUnit.DAYS.between(lastLoginTime, now);
        if (days <= 3) {
            return ActivityScoreWeightEnum.LOGIN_3_DAYS.getWeight();
        } else if (days <= 7) {
            return ActivityScoreWeightEnum.LOGIN_7_DAYS.getWeight();
        } else if (days <= 30) {
            return ActivityScoreWeightEnum.LOGIN_30_DAYS.getWeight();
        } else {
            return ActivityScoreWeightEnum.LOGIN_OVER_30_DAYS.getWeight();
        }
    }

    /**
     * 计算用户信息完整度数量
     */
    private int calculateInfoCompleteCount(User user) {
        int count = 0;
        if (StringUtils.isNotBlank(user.getName())) count++;
        if (StringUtils.isNotBlank(user.getHeadImg())) count++;
        if (StringUtils.isNotBlank(user.getCellphone())) count++;
        if (StringUtils.isNotBlank(user.getSex())) count++;
        if (StringUtils.isNotBlank(user.getCompany())) count++;
        if (StringUtils.isNotBlank(user.getJob())) count++;
        if (StringUtils.isNotBlank(user.getEducation())) count++;
        if (StringUtils.isNotBlank(user.getIndustry())) count++;
        if (StringUtils.isNotBlank(user.getCityCode())) count++;
        return count;
    }

    /**
     * 每天凌晨2点执行分类帖子数量统计
     */
    @Scheduled(cron = "5 0 0 * * ?")
//    @Scheduled(cron = "0 0 2 * * ?")
    public void calculatePostTypeCounts() {
        // 1. 加载完整的分类树
        PostTypeDTO postTypeDTO = systemConfigService.getPostType();
        if (postTypeDTO == null || CollUtil.isEmpty(postTypeDTO.getList())) {
            return; // 没有分类配置，直接返回
        }

        // 2. 递归收集所有分类标签（title）
        List<String> allTags = collectAllTags(postTypeDTO.getList());

        // 3. 批量统计每个标签的帖子数量
        Map<String, Integer> tagCountMap = countTheNumberOfPosts(allTags);

        // 4. 递归计算每个节点的总数（自身 + 所有子孙节点）
        calculateNodeCounts(postTypeDTO.getList(), tagCountMap);

        // 5. 保存更新后的分类配置
        systemConfigService.updatePostType(postTypeDTO);
    }

    /**
     * 递归收集所有分类标签
     */
    private List<String> collectAllTags(List<PostTypeDTO.PostTypeSonDTO> nodeList) {
        List<String> tags = CollUtil.newArrayList();

        if (CollUtil.isEmpty(nodeList)) {
            return tags;
        }

        for (PostTypeDTO.PostTypeSonDTO node : nodeList) {
            if (node == null || node.getTitle() == null) {
                continue;
            }

            // 添加当前节点标签
            tags.add(node.getTitle());

            // 递归收集子节点标签
            tags.addAll(collectAllTags(node.getList()));
        }

        return tags;
    }

    /**
     * 批量统计每个标签的帖子数量
     */
    private Map<String, Integer> countTheNumberOfPosts(List<String> tags) {
        if (CollUtil.isEmpty(tags)) {
            return new HashMap<>(0);
        }

        // 查询所有包含指定标签的帖子并按标签分组统计
        List<Demand> demands = demandService.list(new LambdaQueryWrapper<Demand>()
                .select(Demand::getId, Demand::getTag)
                .in(Demand::getTag, tags)
                .eq(Demand::getStatus, (byte) 1)
        );

        // 按标签统计数量
        return demands.stream()
                .filter(demand -> demand.getTag() != null)
                .collect(Collectors.groupingBy(
                        Demand::getTag,
                        Collectors.collectingAndThen(
                                Collectors.counting(),
                                Long::intValue
                        )
                ));
    }

    /**
     * 递归计算每个节点的总数（自身数量 + 所有子孙节点数量）
     */
    private int calculateNodeCounts(List<PostTypeDTO.PostTypeSonDTO> nodeList, Map<String, Integer> tagCountMap) {
        int total = 0;

        if (CollUtil.isEmpty(nodeList)) {
            return total;
        }

        for (PostTypeDTO.PostTypeSonDTO node : nodeList) {
            if (node == null || node.getTitle() == null) {
                continue;
            }

            // 自身节点的帖子数量
            int selfCount = tagCountMap.getOrDefault(node.getTitle(), 0);

            // 递归计算子节点的总数量
            int childrenCount = calculateNodeCounts(node.getList(), tagCountMap);

            // 当前节点的总数量 = 自身数量 + 所有子节点数量
            int nodeTotal = selfCount + childrenCount;
            node.setCount(nodeTotal);

            // 累加至父节点总数
            total += nodeTotal;
        }

        return total;
    }

}